﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Lib
{
    public class ITAlgo
    {
        public TransactionData database;

        public double minsup;
        public Dictionary<string, double> FrequentItemset;
        public List<FrequentItem> FI = new List<FrequentItem>();

        public ITTree tree = new ITTree();
        
             

        public ITAlgo(double minsupp, TransactionData data)
        {
            minsup = minsupp;
            database = data;
            
        }
        
        public List<ITNode> GetP()
        {
            List<ITNode> P = new List<ITNode>();
            List<TransactionValue> items = new List<TransactionValue>();

            foreach (var item in database.Attributes)
            {
                items.AddRange(item.AttValue.FindAll(x => x.AttIndex == item.Index));
            }

            foreach (var it in items.FindAll(x => x.Count >= minsup))
            {
                List<int> tidsets = new List<int>();
                foreach (var trans in database.Transactions)
                {
                    if (trans.Value.Find(x => x.AttIndex == it.AttIndex && x.Name == it.Name) != null)
                        tidsets.Add(trans.Key);
                    if (tidsets.Count == it.Count)
                        break;
                }
                List<TransactionValue> infoname = new List<TransactionValue>();
                infoname.Add(new TransactionValue(it));
                P.Add(new ITNode { info = new ITPair(infoname, tidsets,database.NumTrans) });
            }
            return P;
        }

        public List<FrequentItem> Charm()
        {
            List<ITNode> P = GetP().OrderBy(x => x.info.support).ToList();
            ITNode root = tree.root;

            Dictionary<List<TransactionValue>, int> fiList = new Dictionary<List<TransactionValue>, int>();

            List<FrequentItem> C = new List<FrequentItem>();
            Charm_Extend(P, C);


            return C;
        }

        private void Charm_Extend(List<ITNode> P, List<FrequentItem> C)
        {
            for (int i = 0; i < P.Count; i++)
            {
                List<ITNode> newP = new List<ITNode>(P);
                P[i].childs = new List<ITNode>();

                List<TransactionValue> X = P[i].getItemsets();

                for (int j = i + 1; j < newP.Count; j++)
                {
                    List<TransactionValue> tam = new List<TransactionValue>(X);
                    tam.AddRange((from x in newP[j].info.infoName
                               where !tam.Contains(x)
                               select x));
                    List<TransactionValue> Xi = tam;
                    List<int> Y = FindSameItem(P[i].getTidsets(), newP[j].getTidsets());
                    if (Y.Count >= minsup)
                    {
                        ITNode newnode = new ITNode { info = new ITPair(Xi, Y, database.NumTrans) };
                        bool val = Charm_Property(newnode, ref X, P[i], newP[j], newP, P, P[i].childs);
                        if (val == true)
                            j--;
                    }
                }
                if (P[i].childs != null)
                    Charm_Extend(P[i].childs, C);
                P[i].childs = new List<ITNode>();
                List<TransactionValue> fiKey = new List<TransactionValue>();
                fiKey.AddRange(P[i].getItemsets());
                if (C.Find(x => x.Name.Except(fiKey.OrderBy(y => y.AttIndex)).ToList().Count == 0) == null)
                {
                    C.Add(new FrequentItem { Name = fiKey.OrderBy(x => x.AttIndex).ToList(), Supp = P[i].getSupp() });
                }
            }
        }

        private bool Charm_Property(ITNode newnode, ref List<TransactionValue> X, ITNode nodei, ITNode nodej, List<ITNode> newP, List<ITNode> P, List<ITNode> P1)
        {
            bool re = false;
            
            List<int> compare = FindSameItem(nodei.getTidsets(), nodej.getTidsets());
            if (newnode.info.support >= minsup)
            {
                if(nodei.getTidsets().Equals(nodej.getTidsets()))//if (EqualBits(tidSeti, tidSetj)) // tinh chat 1
                {
                    newP.Remove(nodej);
                    ReplaceNode(nodei, newnode, P);
                    X = newnode.info.infoName;
                    re = true;
                }
                else if(EqualList(compare,nodei.getTidsets())) //if (EqualBits(tidSeti.And(tidSetj), (tidSeti2))) // tinh chat 2 txi la con cua txj
                {
                    if (nodei.childs.Count != 0)
                    {
                        foreach (var node in nodei.childs)
                        {
                            node.info.infoName.AddRange(nodej.info.infoName);
                            List<TransactionValue> itemset = node.info.infoName;
                            List<int> tidset = node.info.tidsets;
                            ITNode newchild = new ITNode { info = new ITPair(itemset, tidset,database.NumTrans) };
                            newnode.childs.Add(newchild);
                        }
                    }
                    ReplaceNode(nodei, newnode, P);
                    X = newnode.info.infoName;
                }
                else if (EqualList(compare, nodej.getTidsets()))//if (EqualBits(tidSeti.And(tidSetj), (tidSetj2))) // tinh chat 3 txi la cha cua txj
                {
                    newP.Remove(nodej);
                    P1.Add(newnode);
                    X = newnode.info.infoName;
                    re = true;
                }
                else if (!nodei.getTidsets().Equals(nodej.getTidsets()))//if (!EqualBits(tidSeti, tidSetj)) // tinh chat 4 txi khac txj
                {
                    P1.Add(newnode);
                }
            }
            return re;
        }

        private bool EqualBits(BitArray tidSeti, BitArray tidSetj)
        {
            for (int i = 0; i < tidSeti.Length; i++)
            {
                if (tidSeti.Get(i) != tidSetj.Get(i))
                    return false;
            }
            return true;
        }

        private bool EqualList(List<int> a, List<int> b)
        {
            if (a.Count != b.Count)
                return false;
            else
            {
                if (a.Any(x => b.Find(y => y == x) == 0))
                    return false;
            }
            return true;
        }

        private string Sort(string item)
        {
            char[] c = item.ToCharArray();
            Array.Sort(c);
            return new string(c);
        }

        private void ReplaceNode(ITNode nodei, ITNode nodej, List<ITNode> P)
        {
            for (int i = 0; i < P.Count; i++)
            {
                if (P[i] == nodei)
                {
                    P[i] = nodej;
                }
            }
        }

        public BitArray BitAnd2(ITNode a, ITNode b)
        {
            BitArray A = a.info.bitsets;
            BitArray B = b.info.bitsets;
            BitArray r = A.And(B);

            return r;
        }


      
        public List<int> FindSameItem(List<int> a, List<int> b)
        {
            var lista = new List<int>(a);
            var listb = new List<int>(b);
            List<int> r = new List<int>();
            //r = lista.Exists;

            r = lista.Intersect(listb).ToList();
            bool ec = listb.Equals(r);
            bool ac = r.Equals(lista);


            
            return r;
        }

       
        public BitArray CreateBit(List<int> tid)
        {
            BitArray bit = new BitArray(database.NumTrans);
            bit.SetAll(false);

            for (int i = 0; i < bit.Count; i++)
            {
                var find = tid.Find(x => x == (i + 1)); 
                if (find != 0)
                    bit.Set(i, true);
            }
            return bit;
        }

        public List<int> BitToInt(BitArray tidset)
        {
            List<int> result = new List<int>();
            for (int i = 0; i < tidset.Count; i++)
            {
                if (tidset.Get(i) == true)
                    result.Add((i + 1));
            }
            return result;
        }
    }
}
